package org.web3j.crypto.gm;

import java.io.IOException;
import java.math.BigInteger;

import org.bouncycastle.asn1.ASN1EncodableVector;
import org.bouncycastle.asn1.ASN1Encoding;
import org.bouncycastle.asn1.ASN1Integer;
import org.bouncycastle.asn1.ASN1Primitive;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.DERSequence;
import org.bouncycastle.crypto.signers.DSAEncoding;
import org.bouncycastle.util.Arrays;

public class StandardDSAEncoding implements DSAEncoding {

	public static final StandardDSAEncoding INSTANCE = new StandardDSAEncoding();

	public byte[] encode(BigInteger n, BigInteger r, BigInteger s, BigInteger v) throws IOException {
		ASN1EncodableVector vector = new ASN1EncodableVector();
		encodeValue(n, vector, r);
		encodeValue(n, vector, s);
		encodeValue(n, vector, v);
		return new DERSequence(vector).getEncoded(ASN1Encoding.DER);
	}

	public BigInteger[] decode(BigInteger n, byte[] encoding) throws IOException {
		ASN1Sequence seq = (ASN1Sequence) ASN1Primitive.fromByteArray(encoding);
		if (seq.size() == 3) {
			BigInteger r = decodeValue(n, seq, 0);
			BigInteger s = decodeValue(n, seq, 1);
			BigInteger v = decodeValue(n, seq, 2);

			byte[] expectedEncoding = encode(n, r, s, v);
			if (Arrays.areEqual(expectedEncoding, encoding)) {
				return new BigInteger[] { r, s, v };
			}
		}

		throw new IllegalArgumentException("Malformed signature");
	}

	protected BigInteger checkValue(BigInteger n, BigInteger x) {
		if (x.signum() < 0 || (null != n && x.compareTo(n) >= 0)) {
			throw new IllegalArgumentException("Value out of range");
		}

		return x;
	}

	protected BigInteger decodeValue(BigInteger n, ASN1Sequence s, int pos) {
		return checkValue(n, ((ASN1Integer) s.getObjectAt(pos)).getValue());
	}

	protected void encodeValue(BigInteger n, ASN1EncodableVector v, BigInteger x) {
		v.add(new ASN1Integer(checkValue(n, x)));
	}

	@Override
	public byte[] encode(BigInteger n, BigInteger r, BigInteger s) throws IOException {
		ASN1EncodableVector vector = new ASN1EncodableVector();
		encodeValue(n, vector, r);
		encodeValue(n, vector, s);
		return new DERSequence(vector).getEncoded(ASN1Encoding.DER);
	}
	
	public static void main(String[] args) throws IOException {
		// 26014088415083668240768053479980841091609313809911037177760163516650012842621
		// 113613371932068954612552202855053407986598245531398794391302014463731951179644
		// 112571545240194290494337835208982639956724485161181217394637133565382983445170

		BigInteger n = BigInteger.TEN;
		BigInteger r = BigInteger.ONE;
		BigInteger s = BigInteger.ONE;
		BigInteger v = BigInteger.ONE;

		byte[] data = StandardDSAEncoding.INSTANCE.encode(n, r, s, v);
		BigInteger[] decode = StandardDSAEncoding.INSTANCE.decode(n, data);
		System.out.println(decode[0].longValue() == r.longValue());

	}

}